package com.ruoyi.game.websocket.dispatcher;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.game.constant.GameRoomProperties;
import com.ruoyi.game.core.GameRoomCore;
import com.ruoyi.game.service.IGameRoomService;
import com.ruoyi.game.websocket.event.WsIncomingEvent;
import com.ruoyi.game.websocket.handler.GameWebSocketHandler;
import com.ruoyi.game.websocket.message.packet.*;
import com.ruoyi.game.websocket.message.testpacket.DealReq;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import com.ruoyi.game.websocket.message.MessageType;
import com.ruoyi.game.websocket.notify.GameRoomNotifier;

import javax.annotation.PostConstruct;

@Slf4j
@Component
@RequiredArgsConstructor
public class GameWsDispatcher {

    private final IGameRoomService roomSrv;
    private final GameRoomNotifier notifier;
    private final GameRoomProperties gameRoomProperties;
    private final ObjectMapper mapper = new ObjectMapper();
    private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);

    // 存储玩家最后一次收到 ping 的时间
    private final Map<Long, Long> lastPingTimes = new ConcurrentHashMap<>();

    @EventListener
    public void onIncoming(WsIncomingEvent evt) {
        WebSocketSession session = evt.getSession();
        try {
            WsPacket<?> pkt = mapper.readValue(evt.getRawJson(), WsPacket.class);
            dispatch(session, pkt);
        } catch (Exception e) {
            log.error("[Dispatcher] 解析消息失败", e);
            notifier.sendError(session, 4001, "Invalid message format");
        }
    }

    public void dispatch(WebSocketSession session, WsPacket<?> pkt) {
        String type = pkt.getType();
        JsonNode dataNode = mapper.valueToTree(pkt.getData());

        try {
            switch (type) {
                case MessageType.JOIN_ROOM -> {
                    JoinRoomReq req = mapper.treeToValue(dataNode, JoinRoomReq.class);
                    roomSrv.join(session, req);
                }

                case MessageType.READY -> {
                    ReadyReq req = mapper.treeToValue(dataNode, ReadyReq.class);
                    roomSrv.ready(session, req);
                }

                case MessageType.TRUSTEESHIP -> {
                    TrusteeshipReq req = mapper.treeToValue(dataNode, TrusteeshipReq.class);
                    roomSrv.trusteeship(session, req);
                }

                case MessageType.CHAT -> {
                    ChatReq req = mapper.treeToValue(dataNode, ChatReq.class);
                    roomSrv.chat(session, req);
                }

                case MessageType.OUT_CARD -> {
                    OutCardReq req = mapper.treeToValue(dataNode, OutCardReq.class);
                    roomSrv.outPlayerCard(session, req);
                }

                case MessageType.PING -> {
                    // 更新 ping 时间
                    Long playerId = GameWebSocketHandler.getPlayerId(session);
                    lastPingTimes.put(playerId, System.currentTimeMillis());  // 记录最新的 ping 时间
                    notifier.sendPong(session); // 回应 Pong
                }

                case MessageType.LEAVE_ROOM -> {
                    LeaveRoomReq req = mapper.treeToValue(dataNode, LeaveRoomReq.class);
                    roomSrv.leave(session, req);
                }

                case MessageType.DISSOLVE_ROOM -> {
                    DissolveRoomReq req = mapper.treeToValue(dataNode, DissolveRoomReq.class);
                    roomSrv.dissolve(session, req);
                }


                case MessageType.TEST_DEAL -> {
                    DealReq req = mapper.treeToValue(dataNode, DealReq.class);
                    roomSrv.mockShuffleAndDeal(session, req);
                }

                default -> {
                    log.warn("[WS] 未知消息类型: {}", type);
                    notifier.sendError(session, 4000, "Unknown message type: " + type);
                }
            }
        } catch (Exception e) {
            log.error("[WS] 消息处理异常，type={} data={}", type, dataNode, e);
            notifier.sendError(session, 4001, "消息处理异常");
        }
    }

    // 启动定时任务检查玩家 ping 超时
    @PostConstruct
    public void startPingTimeoutChecker() {
        scheduler.scheduleAtFixedRate(this::checkPingTimeouts, 30, 30, TimeUnit.SECONDS);
    }

    private void checkPingTimeouts() {
        long currentTime = System.currentTimeMillis();
        lastPingTimes.forEach((playerId, lastPingTime) -> {
            // 如果超时30秒没有ping，踢出玩家
            if (currentTime - lastPingTime > gameRoomProperties.getPingTimeout()) {
                log.info("[PING TIMEOUT] 玩家 {} 离线，超时 300 秒", playerId);
                handleLost(playerId);
            }
        });
    }

    // 踢出玩家并结束对局
    private void handleLost(Long playerId) {
        GameRoomCore room = roomSrv.requireRoomByPlayerId(playerId);  // 获取玩家所在房间
        if (room == null) return;

        // 结束当前对局
        room.endSet();  // 结束对局

        // 通过房间踢出玩家
        room.kickOutPlayer(playerId, "玩家 " + playerId + " 因离线被踢出", "超时检测离线");  // 踢出玩家

        log.info("[DISSOLVE] 玩家 {} 因超时被踢出房间", playerId);
    }
}